/******************************************************************************************************
Copyright (C) 2012 Mestrelab Research S.L. All rights reserved.

This file is part of the MNova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals draw, Env, JList, mainWindow, MessageBox, mnDebug, Molecule, Multiplet,
 Multiplets, MultipletReportDialog, MultipletTable, NMRAssignments, NMRSpectrum, settings,
 Str, TextDocument*/
/*jslint plusplus: true, indent: 4*/

// MultipletReporter is a base class for JACSMultipletReporter, AngewandteMultipletReporter, RSCMultipletReporter and others.
// To customize reports, edit these classes or implement another ones inherited from MultipletReporter.
function MultipletReporter() {
	"use strict";

	this.name = "Custom";
	this.alias = undefined;
	// fields to be filled in in the child classes
	this.font = "";
	this.header = "";
	this.nucleusTemplate = "";
	this.onlyElementName = false;
	this.withoutJsTemplate = "";
	this.withJsTemplate = "";
	this.rangeTemplate = "";
	this.reportRange = true; // report m's as ranges
	this.allAsRanges = false; // report all multiplets as ranges
	this.pentapletsAsPent = false;
	this.jListTemplate = "";
	this.assignmentsTemplate = "";
	this.jPrecision = 1;
	this.reducedJList = true;
	this.deltaPrecision = 2;
	this.mSeparator = "";
	this.jSeparator = "";
	this.start = "";
	this.end = "";
	this.sortJAscending = false;
	this.sortMultipletsAscending = false;
	this.fullCategory = false;
	this.extendedSolventName = false;
	this.reportJs = true;
	this.assignedMolecules = undefined;
	this.reportAssignments = false;
	this.fillStyle = 'Transparent';
	this.c13AssignmentsWithParenthesis = true;
	this.reportType = 'Multiplet Report';
	this.reporterName = 'J. Am. Chem. Soc.';
	this.fontProperties = MultipletReporter.getDefaultFont();
	this.fontColor = '#000000';
	this.useHtml = true;
}

MultipletReporter.templateSettings = "Multiplet Report/Template";
MultipletReporter.allAsRangesRegKey = "Multiplet Report/All as Ranges";
MultipletReporter.pentapletsAsPentRegKey = "Multiplet Report/Pentaplets As Pent";
MultipletReporter.useRangeSettings = "Multiplet Report/Use Ranges";
MultipletReporter.sortAscendingSettings = "Multiplet Report/Sort Ascending";
MultipletReporter.sortJAscendingSettings = "Multiplet Report/Sort Js Ascending";
MultipletReporter.reducedJListSettings = "Multiplet Report/Reduced J List";
MultipletReporter.extendedSolventNameSettings = "Multiplet Report/Extenced Solvent Name";
MultipletReporter.reportJsSettings = "Multiplet Report/Report Js";
MultipletReporter.reportAssignmentsSettings = "Multiplet Report/Report Assignments";
MultipletReporter.deltaPrecisionSettings = "Multiplet Report/Delta Precision";
MultipletReporter.jPrecisionSettings = "Multiplet Report/J Precision";
MultipletReporter.fillStyleSettings = "Multiplet Report/Fill Style";
MultipletReporter.fontPropertiesSettings = "Multiplet Report/Font Properties";
MultipletReporter.fontColorSettings = "Multiplet Report/Font Color";
MultipletReporter.useHtmlSettings = "Multiplet Report/Use HTML";

MultipletReporter.getSettings = function () {
	"use strict";

	return {
		reporterName : settings.value(MultipletReporter.templateSettings, 'J. Am. Chem. Soc.'),
		reportRange : settings.value(MultipletReporter.useRangeSettings, true),
		allAsRanges : settings.value(MultipletReporter.allAsRangesRegKey, false),
		pentapletsAsPent : settings.value(MultipletReporter.pentapletsAsPentRegKey, false),
		sortMultipletsAscending : settings.value(MultipletReporter.sortAscendingSettings, false),
		sortJAscending : settings.value(MultipletReporter.sortJAscendingSettings, false),
		reducedJList : settings.value(MultipletReporter.reducedJListSettings, true),
		extendedSolventName : settings.value(MultipletReporter.extendedSolventNameSettings, false),
		reportJs : settings.value(MultipletReporter.reportJsSettings, true),
		reportAssignments : settings.value(MultipletReporter.reportAssignmentsSettings, false),
		deltaPrecision : settings.value(MultipletReporter.deltaPrecisionSettings, 2),
		jPrecision : settings.value(MultipletReporter.jPrecisionSettings, 1),
		fillStyle : settings.value(MultipletReporter.fillStyleSettings, 'Transparent'),
		fontProperties : JSON.parse(settings.value(MultipletReporter.fontPropertiesSettings, JSON.stringify(MultipletReporter.getDefaultFont()))),
		fontColor : settings.value(MultipletReporter.fontColorSettings, '#000000'),
		useHtml: settings.value(MultipletReporter.useHtmlSettings, true)
	};
};

MultipletReporter.setSettings = function (aReporter) {
	"use strict";

	settings.setValue(MultipletReporter.templateSettings, aReporter.reporterName);
	settings.setValue(MultipletReporter.allAsRangesRegKey, aReporter.allAsRanges);
	settings.setValue(MultipletReporter.pentapletsAsPentRegKey, aReporter.pentapletsAsPent);
	settings.setValue(MultipletReporter.useRangeSettings, aReporter.reportRange);
	settings.setValue(MultipletReporter.sortAscendingSettings, aReporter.sortMultipletsAscending);
	settings.setValue(MultipletReporter.sortJAscendingSettings, aReporter.sortJAscending);
	settings.setValue(MultipletReporter.extendedSolventNameSettings, aReporter.extendedSolventName);
	settings.setValue(MultipletReporter.reportJsSettings, aReporter.reportJs);
	settings.setValue(MultipletReporter.reducedJListSettings, aReporter.reducedJList);
	settings.setValue(MultipletReporter.reportAssignmentsSettings, aReporter.reportAssignments);
	settings.setValue(MultipletReporter.deltaPrecisionSettings, aReporter.deltaPrecision);
	settings.setValue(MultipletReporter.jPrecisionSettings, aReporter.jPrecision);
	settings.setValue(MultipletReporter.fillStyleSettings, aReporter.fillStyle);
	settings.setValue(MultipletReporter.fontPropertiesSettings, JSON.stringify(aReporter.fontProperties));
	settings.setValue(MultipletReporter.fontColorSettings, aReporter.fontColor);
	settings.setValue(MultipletReporter.useHtmlSettings, aReporter.useHtml);
};

MultipletReporter.reportMultipletsUser = function (createItem, props) {
	"use strict";

	var spectrum, tableSpec, reporter,	multipletTable;

	if (props !== undefined) {
		spectrum = new NMRSpectrum(mainWindow.activeDocument.getItem(props.linkID));
		reporter = MultipletReporter.getReporterByName(props.reporterName);
	}

	if (spectrum === undefined || !spectrum.isValid()) {
		if (mainWindow.hasGui) {
			multipletTable = new MultipletTable(mainWindow.getObject("NMRMultipletsTable")); // Get table of multiplets
			if (multipletTable && multipletTable.isValid()) {
				tableSpec = multipletTable.spectrum();
				if (tableSpec !== undefined && tableSpec.isValid()) {
					spectrum = new NMRSpectrum(multipletTable.spectrum()); // Get spectrum currently shown in the table of multiplets
				}
			}
		}
	}
	if (spectrum === undefined || !spectrum.isValid()) {
		spectrum = new NMRSpectrum(mainWindow.activeDocument.getActiveItem("NMR Spectrum")); // Get active spectrum
	}
	if (spectrum === undefined || !spectrum.isValid()) {
		return undefined;
	}

	if (reporter === undefined) {
		reporter = MultipletReporter.getDefaultReporter();
	}

	if (reporter !== undefined) {
		reporter.readSettings();
		if (props !== undefined) {
			reporter.fontProperties = JSON.parse(JSON.stringify(props.fontProperties));
			reporter.fontColor = props.fontColor;
			reporter.reportRange = props.reportRange;
			reporter.allAsRanges = props.allAsRanges;
			reporter.pentapletsAsPent = props.pentapletsAsPent;
			reporter.sortMultipletsAscending = props.sortHAscending;
			reporter.sortJAscending = props.sortJAscending;
			reporter.reducedJList = props.reducedJList;
			reporter.extendedSolventName = props.extendedSolventName;
			reporter.reportJs = props.reportJs;
			reporter.reportAssignments = props.reportAssignments;
			reporter.deltaPrecision = props.deltaPrecision;
			reporter.jPrecision = props.jPrecision;
			reporter.fillStyle = props.fillStyle;
			reporter.useHtml = props.useHtml;
		}

		return reporter.report(spectrum, createItem);
	}

	return undefined;
};

MultipletReporter.getReporterByName = function (aName) {
	"use strict";

	var i, rep;

	for (i = 0; i < Env.MultipletReportersArray.length; i++) {
		/*jslint eqeq: true*/
		rep = Env.MultipletReportersArray[i];
		if (rep.name === aName || (rep.alias && rep.alias.indexOf(aName) !== -1)) {
			/*jslint eqeq: false*/

			return Env.MultipletReportersArray[i];
		}
	}

	return undefined;
};

MultipletReporter.getDefaultReporter = function (aDefValue) {
	"use strict";

	var retVal, defaultName;

	if (!aDefValue) {
		aDefValue = "J. Am. Chem. Soc.";
	}

	if (Env.DefaultMultipletReporter !== undefined) {
		retVal = MultipletReporter.getReporterByName(Env.DefaultMultipletReporter);
		if (retVal !== undefined) {
			return retVal;
		}
		return MultipletReporter.getReporterByName(aDefValue);
	}
	defaultName = settings.value(MultipletReporter.templateSettings, aDefValue);
	retVal = MultipletReporter.getReporterByName(defaultName);
	if (retVal !== undefined) {
		return retVal;
	}

	return MultipletReporter.getReporterByName(aDefValue);
};

MultipletReporter.getDefaultFont = function () {
	'use strict';

	var font = {};
	font.size = 10;
	font.family = "Times New Roman";

	return font;
};

MultipletReporter.prototype.toString = function () {
	"use strict";

	return "MultipletReporter()";
};

MultipletReporter.prototype.readSettings = function () {
	"use strict";

	this.reporterName = settings.value(MultipletReporter.templateSettings, 'J. Am. Chem. Soc.');
	this.reportRange = settings.value(MultipletReporter.useRangeSettings, true);
	this.allAsRanges = settings.value(MultipletReporter.allAsRangesRegKey, false);
	this.pentapletsAsPent = settings.value(MultipletReporter.pentapletsAsPentRegKey, false);
	this.sortMultipletsAscending = settings.value(MultipletReporter.sortAscendingSettings, false);
	this.sortJAscending = settings.value(MultipletReporter.sortJAscendingSettings, false);
	this.reducedJList = settings.value(MultipletReporter.reducedJListSettings, true);
	this.extendedSolventName = settings.value(MultipletReporter.extendedSolventNameSettings, false);
	this.reportJs = settings.value(MultipletReporter.reportJsSettings, true);
	this.reportAssignments = settings.value(MultipletReporter.reportAssignmentsSettings, false);
	this.deltaPrecision = settings.value(MultipletReporter.deltaPrecisionSettings, 2);
	this.jPrecision = settings.value(MultipletReporter.jPrecisionSettings, 1);
	this.fillStyle = settings.value(MultipletReporter.fillStyleSettings, 'Transparent');
	this.fontProperties = JSON.parse(settings.value(MultipletReporter.fontPropertiesSettings, JSON.stringify(MultipletReporter.getDefaultFont())));
	this.fontColor = settings.value(MultipletReporter.fontColorSettings, '#000000');
	this.useHtml = settings.value(MultipletReporter.useHtmlSettings, true);
};

MultipletReporter.prototype.getProperties = function () {
	"use strict";

	return {
		reportType : this.reportType,
		reporterName : this.reporterName,
		reportRange : this.reportRange,
		allAsRanges : this.allAsRanges,
		pentapletsAsPent : this.pentapletsAsPent,
		sortHAscending : this.sortMultipletsAscending,
		sortJAscending : this.sortJAscending,
		reducedJList : this.reducedJList,
		extendedSolventName: this.extendedSolventName,
		reportJs : this.reportJs,
		reportAssignments : this.reportAssignments,
		deltaPrecision : this.deltaPrecision,
		jPrecision: this.jPrecision,
		fillStyle: this.fillStyle,
		fontProperties : JSON.parse(JSON.stringify(this.fontProperties)),
		fontColor: this.fontColor,
		useHtml: this.useHtml
	};
};

//! Gets the assignment related to the given multiplet
MultipletReporter.prototype.assignmentString = function (multiplet, spectrum) {
	"use strict";

	var i, pageItem, mol,
		assignString = "",
		nmrAssignObject,
		tmpString = "";

	if (this.assignedMolecules === undefined) {
		this.assignedMolecules = spectrum.getNMRAssignedMolecules();
	}

	for (i = 0; i < this.assignedMolecules.length; ++i) {
		pageItem = this.assignedMolecules[i];
		mol = new Molecule(pageItem);
		if (mol.isValid()) {
			nmrAssignObject = new NMRAssignments(mol);
			tmpString = nmrAssignObject.multipletAssignment(multiplet.id);
			if (tmpString.length !== 0) {
				assignString += tmpString + ", ";
			}
		}
	}

	assignString = assignString.replace(/,\s*$/, "");
	return assignString;
};

MultipletReporter.prototype.reportSingleMultiplet = function (multiplet, spectrum) {
	"use strict";

	if (multiplet === undefined || multiplet === null || !multiplet.isValid()) {
		return "";
	}

	var jCount, mTemplate, lastParentRegExp, shiftStr, nucleusStr, numberRegExp, categoryString, multStr, js, ij,
		c1 = "%2", assignmentsValue, assignmentsTemplate, newAssignmentsValue, assignmentAtoms, i, atomNumber,
		nucleusStrForAssignments, flagLabelAtoms,
		jList = new JList(multiplet.jList(this.reducedJList));

	jList.sort(this.sortJAscending);
	jCount = jList.length;

	mTemplate = "";
	if (jCount > 0 && this.reportJs) {
		mTemplate = this.withJsTemplate;
	} else {
		mTemplate = this.withoutJsTemplate;
	}

	if (this.reportAssignments) {
		lastParentRegExp = /\)$/g;
		mTemplate = mTemplate.replace(lastParentRegExp, ", %ASSIGNMENT%)");
	}

	shiftStr = "";
	if (this.allAsRanges || (this.reportRange && multiplet.category === "m")) {
		if (this.sortMultipletsAscending) {
			shiftStr = this.rangeTemplate.replace("%1", multiplet.rangeMin.toFixed(this.deltaPrecision)).replace("%2", multiplet.rangeMax.toFixed(this.deltaPrecision));
		} else {
			shiftStr = this.rangeTemplate.replace("%1", multiplet.rangeMax.toFixed(this.deltaPrecision)).replace("%2", multiplet.rangeMin.toFixed(this.deltaPrecision));
		}
	} else {
		shiftStr = c1.replace("%2", multiplet.delta.toFixed(this.deltaPrecision));
	}

	nucleusStr = spectrum.nucleus(false);
	numberRegExp = /[0-9]*/;
	nucleusStr = nucleusStr.replace(numberRegExp, "");
	if (nucleusStr !== "H") {
		mTemplate = mTemplate.replace("%3", "").replace("%NUCLEUS%", "").replace("( , ", "(").replace(", )", ")").replace(", ,", ",");
	}

	categoryString = "";
	if (this.fullCategory) {
		categoryString = multiplet.fullCategory;
	} else {
		categoryString = multiplet.category;
		if (categoryString.charAt(0) === "m") {
			categoryString = "m";
		}
		if (this.pentapletsAsPent) {
			if (categoryString === "p") {
				categoryString = "pent";
			}
		}
	}

	//to not show (s) for C spectra
	if (nucleusStr !== "H" && (categoryString === "s" || categoryString === "singlet")) {
		mTemplate = mTemplate.replace("%2", "");
		if (!this.c13AssignmentsWithParenthesis || !this.reportAssignments) {
			mTemplate = mTemplate.replace("(", "");
			mTemplate = mTemplate.replace(")", "");
		}
	}

	multStr = mTemplate.replace("%1", shiftStr).replace("%2", categoryString).replace("%3", multiplet.nH).replace("%NUCLEUS%", nucleusStr);

	if (multStr.indexOf("%ASSIGNMENT%") !== -1) {

		//Atom labeling only will be applied for 1H and 13C
		flagLabelAtoms = false;
		if (spectrum.dimCount === 1) {
			nucleusStrForAssignments = spectrum.nucleus(false);
			if (nucleusStrForAssignments === "1H" || nucleusStrForAssignments === "13C") {
				flagLabelAtoms = true;
			}
		}

		//Get the assignments string and prepare its template
		assignmentsTemplate = this.assignmentsTemplate;
		assignmentsValue = this.assignmentString(multiplet, spectrum);

		//Get each atom number and applies the assignments template for each atom
		assignmentAtoms = assignmentsValue.split(", ");
		newAssignmentsValue = "";
		for (i = 0; i < assignmentAtoms.length; i++) {
			atomNumber = assignmentAtoms[i];

			//Skip atom is empty
			if (atomNumber !== "") {
				//Only apply the assignments template for labeling atoms if nucleus is either 1H or 13C
				if (!isNaN(atomNumber) && flagLabelAtoms) {
					newAssignmentsValue += assignmentsTemplate.replace("%1", atomNumber).replace("%2", nucleusStr);
				} else {
					newAssignmentsValue += atomNumber;
				}

				//Add the token separator except for the last one
				if (i !== (assignmentAtoms.length - 1)) {
					newAssignmentsValue += ", ";
				}
			}
		}
		multStr = multStr.replace("%ASSIGNMENT%", newAssignmentsValue);
	}
	if (jCount > 0) {
		js = "";
		for (ij = 0; ij < jCount; ij++) {
			if (js.length !== 0) {
				js += this.jSeparator;
			}
			js += c1.replace("%2", jList.at(ij).toFixed(this.jPrecision));
		}
		multStr = multStr.replace("%4", this.jListTemplate.replace("%1", js));
	}

	multStr = multStr.replace(/(\s*,*\s*)+\)/, ")");
	multStr = multStr.replace(/\((\s*,*\s*)+/, "(");
	multStr = multStr.replace(/(\s*,*\s*)+$/, "");
	multStr = multStr.replace(/,\s*,/, ",");
	multStr = multStr.replace(/\s*\(\s*\)\s*/, "");

	return multStr;
};

MultipletReporter.prototype.report = function (spectrum, createItem) {
	"use strict";

	var count, multiplets, nucleusString, result, i, multiplet, fontString,
		italic = "",
		bold = "",
		decoration = "", txtDoc;

	this.assignedMolecules = undefined;

	multiplets = new Multiplets(spectrum.multiplets()); // get multiplets from spectrum
	multiplets.sort(this.sortMultipletsAscending);
	count = multiplets.count;
	if (!count) {// if no multiplets then exit
		return "";
	}


	// Generate HTML report string
	nucleusString = "";
	if (this.onlyElementName) {
		nucleusString = Str.removeNumbers(spectrum.nucleus());
	} else {
		nucleusString = spectrum.nucleus(true);
	}
	nucleusString = this.nucleusTemplate.replace("%1", nucleusString);

	if (this.fontProperties.italic) {
		italic = "italic";
	}
	if (this.fontProperties.bold) {
		bold = "bold";
	}
	if (this.fontProperties.underline) {
		decoration = "underline ";
	}
	if (this.fontProperties.strikeOut) {
		decoration += "line-through";
	}

	fontString = this.start.replace("%1", this.fontProperties.size).
							replace("%2", this.fontProperties.family).
							replace("%3", italic).
							replace("%4", bold).
							replace("%5", decoration).
							replace("%6", this.fontColor);
	result = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><title>Multiplet Report</title></head><body>" + fontString + this.header.replace("%1", nucleusString).replace("%2", Math.round(spectrum.frequency())).replace("%3", Str.solventStr2Html(spectrum.solventName(this.extendedSolventName)));
	for (i = 0; i < count; i++) {
		if (multiplets.at(i) && multiplets.at(i).isValid()) {
			multiplet = new Multiplet(multiplets.at(i));
			if (multiplet && multiplet.category !== "") {
				result += this.reportSingleMultiplet(multiplet, spectrum);

				if (i < count - 1) {
					result += this.mSeparator;
				}
			}
		}
	}
	result += this.end + "</body></html>";

	if (!this.useHtml) {
		result = result.replace(/&ndash;/g, "-");

		txtDoc = new TextDocument();
		txtDoc.setHtml(result);
		result = txtDoc.toPlainText();
	}

	if (createItem) {
		this.createItem(spectrum, result);
	}

	return result;
};

MultipletReporter.prototype.createItem = function (aSpec, aReport, aBig) {
	"use strict";

	var oldLTCount, textItem, props, heightRatio = 0.25;
	if (aBig) {
		heightRatio = 0.50;
	}

	oldLTCount = mainWindow.activeWindow().itemCount("Layout Template");

	props = this.getProperties();
	textItem = draw.text(aReport, "Report Special", "Multiplets," + aSpec.subtype, this.useHtml, aSpec.uuid, props);

	if (this.fillStyle !== undefined && this.fillStyle === "White") {
		textItem.color = "#ffffff";
		textItem.opacity = 100;
	} else {
		textItem.opacity = 0;
	}
	textItem.update();
	if (oldLTCount === mainWindow.activeWindow().itemCount("Layout Template")) {
		textItem.left = aSpec.left;
		textItem.right = (aSpec.left + aSpec.right) / 2.0;
		textItem.top = aSpec.top;
		textItem.bottom = aSpec.top + (aSpec.bottom - aSpec.top) * heightRatio;
	}

	mainWindow.activeWindow().update();
};


function customizeReportMultipletsUser() {
	"use strict";

	if (Env.ReportMultipletsUserSetup !== undefined) {
		return Env.ReportMultipletsUserSetup();
	}
	if (Env.DefaultMultipletReporter !== undefined) {
		MessageBox.information("Using Default Multiplet Reporter: " + Env.DefaultMultipletReporter.toString(), MessageBox.Ok, MessageBox.NoButton, MessageBox.NoButton, "Information");
	} else {
		var diag = new MultipletReportDialog(Env.MultipletReportersArray, true);
		diag.exec();
	}
}

function automaticSingleMultipletReport(aMultiplet, aSpectrum) {
	"use strict";

	var reporter = MultipletReporter.getDefaultReporter();

	reporter.readSettings();
	return reporter.reportSingleMultiplet(aMultiplet, aSpectrum);
}

function reportMultipletsUser(createItem, showDialog, props) {
	"use strict";

	if (Env.ReportMultipletsUser !== undefined) {
		return Env.ReportMultipletsUser(createItem);
	}

	return MultipletReporter.reportMultipletsUser(createItem, props);
}

function automaticMultipletReport() {
	"use strict";

	return reportMultipletsUser(false);
}

//Function to create reports for the active spectrum
function reportMultiplets(reporter) {
	"use strict";

	var spectrum = new NMRSpectrum(mainWindow.activeDocument.getActiveItem("NMR Spectrum"));

	reporter.report(spectrum, true);
}

//Multiplet Labels

//Base class to create Multiplet Label Reports (to be shown in the UI)
//Subclasses must have a report function
//the report function will receive three parameters:
//the first is the multiplet to be reported
//the second is the number of decimals to be used
//the third is the element symbol
//the fourth is the normalization factor used in the multiplet list
//the fifth is the number of decimals to be used when showing the integral value
//the sixth is the number of decimals to be used when showing Js
function MultipletLabelReporter() {
	"use strict";

	this.name = "Custom Reporter"; //name used to identify the reporter
	this.description = ""; //Not needed. Small description of the reporter. If not null this will be shown in NMR Spectrum Properties->Multiplets
}
MultipletLabelReporter.prototype.toString = function () {
	"use strict";

	return this.name;
};

//MNova Default: Name (Category) Shift
function MNovaMultipletLabelReporter() {
	"use strict";

	MultipletLabelReporter.call(this);
	this.name = "MNova Default";
	this.description = "Name (Category) / Shift";
}

MNovaMultipletLabelReporter.prototype = new MultipletLabelReporter();

/*jslint unparam: true*/
MNovaMultipletLabelReporter.prototype.report = function (aMultiplet, aNumberDecimals, aElementSymbol, aNormValue, aIntegralNumberDecimals, aJNumDec) {
	"use strict";

	var multLabel,
		categoryString = aMultiplet.category;

	if (categoryString.charAt(0) === "m") {
		categoryString = "m";
	}

	multLabel = "%1 (%2)\n%3";
	multLabel = multLabel.replace("%1", aMultiplet.name).replace("%2", categoryString).replace("%3", aMultiplet.delta.toFixed(aNumberDecimals));

	return multLabel;

};
/*jslint unparam: false*/

//MNova Default with J's: Name (Category) Shift J's
function MNovaMultipletLabelReporterWithJ() {
	"use strict";

	MultipletLabelReporter.call(this);

	this.name = "MNova Default with J's";
	this.description = "Name (Category) / Shift / J's";
}

MNovaMultipletLabelReporterWithJ.prototype = new MultipletLabelReporter();

/*jslint unparam: true*/
MNovaMultipletLabelReporterWithJ.prototype.report = function (aMultiplet, aNumberDecimals, aElementSymbol, aNormValue, aIntegralNumberDecimals, aJNumDec) {
	"use strict";

	var jList, js, jListString, ij,
		categoryString = aMultiplet.category,
		multLabel = "%1 (%2)\n%3";

	if (categoryString.charAt(0) === "m") {
		categoryString = "m";
	}

	multLabel = multLabel.replace("%1", aMultiplet.name).replace("%2", categoryString).replace("%3", aMultiplet.delta.toFixed(aNumberDecimals));
	jList = aMultiplet.jList(true);//sort descending
	jList.sort(false);
	if (jList.length !== 0) {
		multLabel += "\n%4";
		js = "J(%1)";
		jListString = "";
		for (ij = 0; ij < jList.length; ij++) {
			if (jListString.length !== 0) {
				jListString += ", ";
			}
			jListString += jList.at(ij).toFixed(aJNumDec);
		}
		multLabel = multLabel.replace("%4", js.replace("%1", jListString));
	}

	return multLabel;
};
/*jslint unparam: false*/

//Multiplet Name Only: Name
function MNovaMultipletLabelReporterName() {
	"use strict";

	MultipletLabelReporter.call(this);

	this.name = "Multiplet Name Only";
	this.description = "Name";
}

MNovaMultipletLabelReporterName.prototype = new MultipletLabelReporter();
MNovaMultipletLabelReporterName.prototype.report = function (aMultiplet) {
	"use strict";
	return aMultiplet.name;
};

//Name (Category) Shift Nuclides
function MNovaMultipletLabelReporterWithNucl() {
	"use strict";

	MultipletLabelReporter.call(this);

	this.name = "MNova Default with Nuclides Count";
	this.description = "Name (Category) / Shift / Nuclides";
}
MNovaMultipletLabelReporterWithNucl.prototype = new MultipletLabelReporter();

/*jslint unparam: true*/
MNovaMultipletLabelReporterWithNucl.prototype.report = function (aMultiplet, aNumberDecimals, aElementSymbol, aNormValue, aIntegralNumberDecimals, aJNumDec) {
	"use strict";

	var categoryString = aMultiplet.category,
		multLabel = "%1 (%2)\n%3";

	if (categoryString.charAt(0) === "m") {
		categoryString = "m";
	}
	multLabel = multLabel.replace("%1", aMultiplet.name).replace("%2", categoryString).replace("%3", aMultiplet.delta.toFixed(aNumberDecimals));
	if (aMultiplet.nH !== 0) {
		multLabel += "\n";
		multLabel += "#" + aElementSymbol + "=" + aMultiplet.nH;
	}
	return multLabel;
};
/*jslint unparam: false*/

//Name (Category) Shift Integrals
//the integrals will be normalized
function MNovaMultipletLabelReporterWithIntegrals() {
	"use strict";

	MultipletLabelReporter.call(this);

	this.name = "MNova Default with Integral";
	this.description = "Name (Category) / Shift / Integral";
}
MNovaMultipletLabelReporterWithIntegrals.prototype = new MultipletLabelReporter();

/*jslint unparam: true*/
MNovaMultipletLabelReporterWithIntegrals.prototype.report = function (aMultiplet, aNumberDecimals, aElementSymbol, aNormValue, aIntegralNumberDecimals, aJNumDec) {
	"use strict";

	var categoryString = aMultiplet.category,
		multLabel = "%1 (%2)\n%3";
	if (categoryString.charAt(0) === "m") {
		categoryString = "m";
	}
	multLabel = multLabel.replace("%1", aMultiplet.name).replace("%2", categoryString).replace("%3", aMultiplet.delta.toFixed(aNumberDecimals));
	if (aNormValue !== 0) {
		multLabel += "\n";
		multLabel += "I=" + aMultiplet.integralValue(aNormValue).toFixed(aIntegralNumberDecimals);
	}
	return multLabel;
};
/*jslint unparam: false*/

//Name (Category) Shift NN Integrals
//the integrals will be normalized
function MNovaMultipletLabelReporterWithNuclidesIntegrals() {
	"use strict";

	MultipletLabelReporter.call(this);

	this.name = "MNova Default with Nuclides and Integral";
	this.description = "Name (Category) / Shift / NN / Integral";
}
MNovaMultipletLabelReporterWithNuclidesIntegrals.prototype = new MultipletLabelReporter();

/*jslint unparam: true*/
MNovaMultipletLabelReporterWithNuclidesIntegrals.prototype.report = function (aMultiplet, aNumberDecimals, aElementSymbol, aNormValue, aIntegralNumberDecimals, aJNumDec) {
	"use strict";

	var categoryString = aMultiplet.category,
		multLabel = "%1 (%2)\n%3";
	if (categoryString.charAt(0) === "m") {
		categoryString = "m";
	}
	multLabel = multLabel.replace("%1", aMultiplet.name).replace("%2", categoryString).replace("%3", aMultiplet.delta.toFixed(aNumberDecimals));
	if (aMultiplet.nH !== 0) {
		multLabel += "\n";
		multLabel += "#" + aElementSymbol + "=" + aMultiplet.nH;
	}
	if (aNormValue !== 0) {
		multLabel += "\n";
		multLabel += "I=" + aMultiplet.integralValue(aNormValue).toFixed(aIntegralNumberDecimals);
	}

	return multLabel;
};
/*jslint unparam: false*/

//Add the multiplet label reporters to the MultipletLabelFunctions environment variable to make them available from the UI
if (Env.MultipletLabelFunctions) {
	Env.MultipletLabelFunctions.push(new MNovaMultipletLabelReporter());
	Env.MultipletLabelFunctions.push(new MNovaMultipletLabelReporterWithNucl());
	Env.MultipletLabelFunctions.push(new MNovaMultipletLabelReporterWithIntegrals());
	Env.MultipletLabelFunctions.push(new MNovaMultipletLabelReporterWithNuclidesIntegrals());
	Env.MultipletLabelFunctions.push(new MNovaMultipletLabelReporterWithJ());
	Env.MultipletLabelFunctions.push(new MNovaMultipletLabelReporterName());
}

function getLabelReporter(aName) {
	"use strict";

	var i;

	for (i = 0; i < Env.MultipletLabelFunctions.length; i++) {
		if (Env.MultipletLabelFunctions[i].name === aName) {
			return Env.MultipletLabelFunctions[i];
		}
	}
	return undefined;
}

function getLabelReportNameList() {
	"use strict";

	var i, namesList = [];

	for (i = 0; i < Env.MultipletLabelFunctions.length; i++) {
		namesList.push(Env.MultipletLabelFunctions[i].name);
	}
	return namesList;
}

function getLabelReportDescriptionList() {
	"use strict";

	var i, descList = [];

	for (i = 0; i < Env.MultipletLabelFunctions.length; i++) {
		descList.push(Env.MultipletLabelFunctions[i].description);
	}
	return descList;
}
